/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.analyzer;

import java.lang.reflect.Field;
import java.util.Comparator;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ImportMap;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.CGAnnotationFactory;
import org.jfab.core.factory.CGFieldFactory;
import org.jfab.core.factory.CGTypeFactory;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.model.CGField;
import org.jfab.core.model.CGType;
import org.jfab.core.model.CGTypeUtilities;
import org.jfab.core.model.Modifier;
import org.jfab.core.model.TypeFactory;

/**
 * Provides a field analyzer.
 */
public final class FieldAnalyzer extends AbstractAnalyzer<CGField, Field>
{
    /** Field factory. */
    private final CGFieldFactory _fieldFactory;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeFactory Type factory.
     * @param cgTypeUtils Code generator type utilities. (required)
     * @param cgTypeFactory Code generator type factory.
     * @param stringFactory String factory.
     * @param annotationFactory Annotation factory.
     * @param comparator Comparator.
     * @param fieldFactory Field factory.
     */
    public FieldAnalyzer(final TypeFactory typeFactory,
            final CGTypeUtilities cgTypeUtils,
            final CGTypeFactory cgTypeFactory,
            final StringFactory stringFactory,
            final CGAnnotationFactory annotationFactory,
            final Comparator<CGField> comparator,
            final CGFieldFactory fieldFactory)
    {
        super(typeFactory, cgTypeUtils, cgTypeFactory, stringFactory,
                annotationFactory, comparator);

        _fieldFactory = fieldFactory;
    }

    @Override
    public CGField analyze(final Field field, final String javaSource)
    {
        Validate.notNull(field, "field is null");

        CGField answer = null;

        final String fieldName = field.getName();

        if (!fieldName.startsWith("$"))
        {
            final CGTypeList referencedTypes = null;
            CommentList commentLines = null;
            CGAnnotationList annotations = null;
            final ModifierList modifiers = new ModifierList(
                    field.getModifiers());
            CGType fieldType = getCgTypeFactory().createType(field.getType());
            String initialValue = null;
            final Boolean isGetter = null;
            final Boolean isSetter = null;
            final boolean isUsedInIdentity = !modifiers.isStatic()
                    && !modifiers.isTransient();

            if (javaSource != null)
            {
                final int fieldIndex = javaSource.indexOf(fieldName);

                if (fieldIndex >= 0)
                {
                    final String preField = javaSource.substring(0, fieldIndex);
                    commentLines = parseCommentLines(preField);
                    annotations = getAnnotations(preField);
                    fieldType = parseFieldType(field.getType(), javaSource,
                            preField);

                    final String postField = javaSource.substring(fieldIndex
                            + fieldName.length());
                    initialValue = parseInitialValue(postField);
                }
            }

            answer = getFieldFactory().createField(referencedTypes,
                    commentLines, annotations, modifiers, fieldType, fieldName,
                    initialValue, isGetter, isSetter, isUsedInIdentity);
        }

        return answer;
    }

    /**
     * @return the fieldFactory
     */
    private CGFieldFactory getFieldFactory()
    {
        return _fieldFactory;
    }

    /**
     * @param fieldClass Field class.
     * @param sourceCode Source code.
     * @param preField Pre-field source code.
     * 
     * @return field type.
     */
    private CGType parseFieldType(final Class<?> fieldClass,
            final String sourceCode, final String preField)
    {
        CGType answer = null;

        final String packageName = parsePackageName(sourceCode);
        final ImportMap importMap = analyzeImports(sourceCode);
        String codeFragment = StringUtils.substringAfterLast(preField, "{");

        String text = StringUtils.substringAfterLast(codeFragment, ";");

        if (StringUtils.isNotEmpty(text))
        {
            codeFragment = text;
        }

        text = StringUtils.substringAfterLast(codeFragment, "*/");

        if (StringUtils.isNotEmpty(text))
        {
            codeFragment = text;
        }

        if (codeFragment.endsWith("_"))
        {
            codeFragment = codeFragment.substring(0, codeFragment.length() - 1);
        }

        codeFragment = codeFragment.trim();

        int index0 = 0;
        int index1 = codeFragment.indexOf(' ');
        boolean isModifierFound = false;

        while ((index0 >= 0) && (index1 >= 0))
        {
            text = codeFragment.substring(index0, index1);

            final Modifier modifier = Modifier.valueOfName(text);

            if (modifier != null)
            {
                index0 = index1 + 1;
                index1 = codeFragment.indexOf(' ', index0);

                if (index1 < 0)
                {
                    index1 = codeFragment.length();
                }

                isModifierFound = true;
            }
            else
            {
                if (isModifierFound)
                {
                    codeFragment = codeFragment.substring(index0);
                    index0 = -1;
                    index1 = -1;
                }
                else
                {
                    index0 = index1 + 1;
                    index1 = codeFragment.indexOf(' ', index0);
                }
            }
        }

        answer = getCgTypeFactory().createType(packageName, importMap,
                codeFragment);

        return answer;
    }

    /**
     * @param line Line.
     * 
     * @return the initial value string (if any) parsed from the given
     *         parameter.
     */
    private String parseInitialValue(final String line)
    {
        String answer = null;
        final String key1 = ";";
        final int initialValueIndex1 = line.indexOf(key1);

        if (initialValueIndex1 >= 0)
        {
            final String key0 = "=";
            final int initialValueIndex0 = line.indexOf(key0);

            if (initialValueIndex0 < initialValueIndex1)
            {
                answer = line.substring(initialValueIndex0 + key0.length(),
                        initialValueIndex1);
                answer = answer.trim();
            }
        }

        return answer;
    }
}
